home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / iguana / vts139b / lib / modloade.pas < prev    next >
Pascal/Delphi Source File  |  1993-10-23  |  14KB  |  453 lines

  1. UNIT ModLoader;
  2.  
  3. INTERFACE
  4.  
  5. USES Objects, SongUnit;
  6.  
  7.  
  8.  
  9.  
  10. PROCEDURE LoadModFileFormat  (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  11.  
  12.  
  13.  
  14.  
  15. IMPLEMENTATION
  16.  
  17. USES SongElements, Heaps, AsciiZ;
  18.  
  19.  
  20.  
  21.  
  22. {----------------------------------------------------------------------------}
  23. { Internal definitions. Format of the files.                                 }
  24. {____________________________________________________________________________}
  25.  
  26. TYPE
  27.   TModFileMagic = ARRAY[0..3] OF CHAR;
  28.  
  29. CONST
  30.   Mod31MagicM_K_ : TModFileMagic = ( 'M', '.', 'K', '.' );
  31.   Mod31MagicFLT4 : TModFileMagic = ( 'F', 'L', 'T', '4' );
  32.   Mod31Magic6CHN : TModFileMagic = ( '6', 'C', 'H', 'N' );
  33.   Mod31Magic8CHN : TModFileMagic = ( '8', 'C', 'H', 'N' );
  34.  
  35. TYPE
  36.  
  37.   { Instrument in a MOD file. 30 bytes. }
  38.  
  39.   TModFileInstrument = RECORD
  40.     Name       : ARRAY [1..22] OF CHAR; { AsciiZ string, name of the instrument. }
  41.     Len        : WORD;                  { Length of the sample DIV 2.            }
  42.     FineTune,                           { Fine tuning value.                     }
  43.     Vol        : BYTE;                  { Default volume.                        }
  44.     LoopStart,                          { Offset of the loop DIV 2.              }
  45.     LoopLen    : WORD;                  { Length of the loop DIV 2.              }
  46.   END;
  47.  
  48.   { Note in the file. 4 bytes. }
  49.  
  50.   PModFileNote = ^TModFileNote;
  51.   TModFileNote = RECORD
  52.     CASE INTEGER OF
  53.       1: (l              : LONGINT);
  54.       2: (w1, w2         : WORD);
  55.       3: (b1, b2, b3, b4 : BYTE);
  56.   END;
  57.  
  58.   PModFilePattern = ^TModFilePattern;
  59.   TModFilePattern =
  60.     RECORD
  61.       CASE BYTE OF
  62.         4 : ( Patt4 : ARRAY [0..63] OF ARRAY [1..4] OF TModFileNote );
  63.         5 : ( Patt5 : ARRAY [0..63] OF ARRAY [1..5] OF TModFileNote );
  64.         6 : ( Patt6 : ARRAY [0..63] OF ARRAY [1..6] OF TModFileNote );
  65.         7 : ( Patt7 : ARRAY [0..63] OF ARRAY [1..7] OF TModFileNote );
  66.         8 : ( Patt8 : ARRAY [0..63] OF ARRAY [1..8] OF TModFileNote );
  67.     END;                                                   
  68.  
  69.   { 15 samples module header format. 600 bytes. }
  70.  
  71.   PModFile15 = ^TModFile15;
  72.   TModFile15 = RECORD
  73.     Name        : ARRAY [1..20] OF CHAR;               { AsciiZ song name.                   }
  74.     Samples     : ARRAY [1..15] OF TModFileInstrument; { Instruments.                        }
  75.     SongLen     : BYTE;                                { Length of the sequency of the song. }
  76.     SongRep     : BYTE;                                { Song loop start position.           }
  77.     PatternList : ARRAY [0..127] OF BYTE;              { Pattern sequencies.                 }
  78.   END;
  79.  
  80.   { 31 samples module header format. 1084 bytes. }
  81.  
  82.   PModFile31 = ^TModFile31;
  83.   TModFile31 = RECORD
  84.     Name        : ARRAY [1..20] OF CHAR;               { AsciiZ song name.                   }
  85.     Samples     : ARRAY [1..31] OF TModFileInstrument; { Instruments.                        }
  86.     SongLen     : BYTE;                                { Length of the sequency of the song. }
  87.     SongRep     : BYTE;                                { Song loop start position.           }
  88.     PatternList : ARRAY [0..127] OF BYTE;              { Pattern sequencies.                 }
  89.     Magic       : TModFileMagic;                       { Magic number ("M.K.", "FLT4", etc.) }
  90.   END;
  91.  
  92.  
  93.  
  94.  
  95.  
  96. PROCEDURE ProcessPatterns(VAR Song: TSong; VAR St: TStream; Num: WORD);
  97.   VAR
  98.     Patt      : TModFilePattern;
  99.     FullTrack : TFullTrack;
  100.     Pattern   : PPattern;
  101.     Track     : PTrack;
  102.     i, j      : WORD;
  103.     n, t      : WORD;
  104.     l         : LONGINT;
  105.   BEGIN
  106.     t := 1;
  107.     FOR n := 1 TO Num DO
  108.       BEGIN
  109.         Pattern := Song.GetPattern(n);
  110.         IF Pattern = NIL THEN
  111.           BEGIN
  112.             Song.Status := msOutOfMemory;
  113.             EXIT;
  114.           END;
  115.  
  116.         WITH Pattern^.Patt^ DO
  117.           BEGIN
  118.             NNotes   := 64;
  119.             NChans   := Song.NumChannels;
  120.             Tempo    := 0;
  121.             BPM      := 0;
  122.           END;
  123.  
  124.         l := St.GetPos;
  125.         St.Read(Patt, 64*4*Song.NumChannels);
  126.  
  127.         IF St.Status <> stOk THEN
  128.           BEGIN
  129.             Song.Status := msFileTooShort;
  130.             EXIT;
  131.           END;
  132.  
  133.         CASE Song.NumChannels OF
  134.           4 : FOR i := 63 DOWNTO 0 DO
  135.                 FOR j := Song.NumChannels DOWNTO 1 DO
  136.                   Patt.Patt8[i][j] := Patt.Patt4[i][j];
  137.           5 : FOR i := 63 DOWNTO 0 DO
  138.                 FOR j := Song.NumChannels DOWNTO 1 DO
  139.                   Patt.Patt8[i][j] := Patt.Patt5[i][j];
  140.           6 : FOR i := 63 DOWNTO 0 DO
  141.                 FOR j := Song.NumChannels DOWNTO 1 DO
  142.                   Patt.Patt8[i][j] := Patt.Patt6[i][j];
  143.           7 : FOR i := 63 DOWNTO 0 DO
  144.                 FOR j := Song.NumChannels DOWNTO 1 DO
  145.                   Patt.Patt8[i][j] := Patt.Patt7[i][j];
  146.         END;
  147.  
  148.         FOR j := 1 TO Song.NumChannels DO
  149.           BEGIN
  150.             FillChar(FullTrack, SizeOf(FullTrack), 0);
  151.  
  152.             FOR i := 0 TO 63 DO
  153.               WITH FullTrack[i], Patt.Patt8[i][j] DO
  154.                 BEGIN
  155.                   Command     := TModCommand((b3 AND $F) + 1);
  156.                   IF Command = mcExtended THEN
  157.                     BEGIN
  158.                       Parameter := b4 AND $F;
  159.                       Command   := TModCommand(($11 + (b4 SHR 4)));
  160.                     END
  161.                   ELSE IF (Command = mcArpeggio) AND (b4 = 0) THEN
  162.                     BEGIN
  163.                       Parameter := 0;
  164.                       Command   := mcNone;
  165.                     END
  166.                   ELSE
  167.                     Parameter := b4;
  168.  
  169.                   Period     := b2 + (WORD(b1 AND $7) SHL 8);
  170.                   Instrument := (b3 SHR 4) + (b1 AND 16);
  171.  
  172.                   IF ((Command = mcEndPattern) OR (Command = mcJumpPattern)) AND
  173.                      (Pattern^.Patt^.NNotes > i + 1) THEN
  174.                     Pattern^.Patt^.NNotes := i + 1;
  175.  
  176.                   IF (Command = mcSetVolume) AND (Parameter > $40) THEN
  177.                     Parameter := $40;
  178.  
  179.                   IF (Command = mcJumpPattern) THEN
  180.                     Parameter := (Parameter AND $0F) +
  181.                                  (Parameter SHR   4)*10 + 1;
  182.  
  183.                   IF (Command = mcEndPattern) THEN
  184.                     Parameter := (Parameter AND 63) + 1;
  185.                 END;
  186.  
  187.             Track := Song.GetTrack(t);
  188.             IF Track = NIL THEN
  189.               BEGIN
  190.                 Song.Status := msOutOfMemory;
  191.                 EXIT;
  192.               END;
  193.  
  194.             Track^.SetFullTrack(FullTrack);
  195.  
  196.             Pattern^.Patt^.Channels[j] := t;
  197.  
  198.             INC(t);
  199.           END;
  200.  
  201.       END;
  202.   END;
  203.  
  204.  
  205. PROCEDURE ProcessInstruments(VAR Song: TSong; VAR St: TStream; Mod31: TModFile31);
  206.   CONST
  207.     MinLoop = 1024;
  208.     FineTuneTable : ARRAY[0..15] OF WORD = ( $43CD,$444B,$44CA,$454A,
  209.                                              $45CA,$464C,$46CF,$4752,
  210.                                              $4000,$4076,$40EE,$4166,
  211.                                              $41E0,$425A,$42D5,$4351 );
  212.  
  213.   VAR
  214.     Instrument : TInstrumentRec;
  215.     Instr      : PInstrument;
  216.     i, j, k    : WORD;
  217.   BEGIN
  218.     FOR i := 1 TO 31 DO
  219.       WITH Instrument DO
  220.         BEGIN
  221.           FillChar(Instrument, SizeOf(Instrument), 0);
  222.  
  223.           Instr := Song.GetInstrument(i);
  224.           IF Instr = NIL THEN
  225.             BEGIN
  226.               Song.Status := msOutOfMemory;
  227.               EXIT;
  228.             END;
  229.  
  230.           Instr^.SetName(StrASCIIZ(Mod31.Samples[i].Name, 22));
  231.  
  232.           Len  := LONGINT(SWAP(Mod31.Samples[i].Len)      ) SHL 1;
  233.  
  234.           IF Len > St.GetSize - St.GetPos THEN
  235.             BEGIN
  236.               Len := St.GetSize - St.GetPos;
  237.               Song.Status := msFileTooShort;
  238.             END;
  239.  
  240.           IF Len > 0 THEN
  241.             BEGIN
  242.  
  243.               Reps := LONGINT(SWAP(Mod31.Samples[i].LoopStart)) SHL 1;
  244.               Repl := LONGINT(SWAP(Mod31.Samples[i].LoopLen)  ) SHL 1;
  245.               Vol  :=              Mod31.Samples[i].Vol;
  246.  
  247.               IF Repl        > Len THEN Repl := Len;
  248.               IF Reps + Repl > Len THEN Reps := Len - Repl;
  249.  
  250.               IF Mod31.Samples[i].Vol > $40 THEN
  251.                 Mod31.Samples[i].Vol := $40;
  252.  
  253.               IF Vol > $40 THEN
  254.                 Vol := $40;
  255.  
  256.               NAdj := FineTuneTable[0];
  257.               DAdj := FineTuneTable[Mod31.Samples[i].FineTune AND $F];
  258.  
  259.               IF Len <= MaxSample THEN
  260.                 BEGIN
  261.                   IF Repl > 4 THEN
  262.                     FullHeap.HGetMem(POINTER(Data), Len+Repl*(MinLoop DIV Repl))
  263.                   ELSE
  264.                     FullHeap.HGetMem(POINTER(Data), Len);
  265.  
  266.                   IF Data = NIL THEN BEGIN
  267.                     Song.Status := msOutOfMemory;
  268.                     EXIT;
  269.                   END;
  270.  
  271.                   St.Read(Data^, Len);
  272.  
  273.                   IF Repl > 4 THEN
  274.                     FOR j := 1 TO MinLoop DIV Repl DO
  275.                       FOR k := 0 TO Repl - 1 DO
  276.                         Data^[Reps + j*Repl + k] := Data^[Reps + k];
  277.  
  278.                   IF Repl > 4 THEN
  279.                     BEGIN
  280.                       Len  := Len  + Repl*(MinLoop DIV Repl);
  281.                       Repl := Repl + Repl*(MinLoop DIV Repl);
  282.                     END;
  283.  
  284.                   IF St.Status <> stOk THEN BEGIN
  285.                     Song.Status := msFileDamaged;
  286.                     EXIT;
  287.                   END;
  288.  
  289. {
  290.                   FOR w := 0 TO Len - 1 DO
  291.                     IF Instruments[i].data^[w] = -128 THEN
  292.                       Instruments[i].data^[w] := -127;
  293. }
  294.                 END
  295.               ELSE
  296.                 BEGIN
  297.                   FullHeap.HGetMem(POINTER(Data), MaxSample);
  298.                   FullHeap.HGetMem(POINTER(Xtra), Len-MaxSample);
  299.  
  300.                   IF (Data = NIL) OR (Xtra = NIL) THEN BEGIN
  301.                     Song.Status := msOutOfMemory;
  302.                     EXIT;
  303.                   END;
  304.  
  305.                   St.Read(Data^, MaxSample);
  306.                   St.Read(Xtra^, Len-MaxSample);
  307.  
  308.                   IF St.Status <> 0 THEN BEGIN
  309.                     Song.Status := msFileDamaged;
  310.                     EXIT;
  311.                   END;
  312.                 END;
  313.  
  314.               Instr^.Change (@Instrument);
  315.             END;
  316.         END;
  317.   END;
  318.  
  319.  
  320. PROCEDURE LoadMod(VAR Song: TSong; VAR St: TStream; VAR Mod31: TModFile31);
  321.   VAR
  322.     j, k,
  323.     i, w       : WORD;
  324.     IsMod31    : BOOLEAN;
  325.     NumberOfPatterns : WORD;
  326.  
  327.   BEGIN
  328.  
  329.     { Initial checkings to see if it's a real MOD. }
  330.  
  331.     Song.Status := msFileDamaged;
  332.  
  333.     FOR i := 0 TO 127 DO
  334.       IF Mod31.PatternList[i] > 63 THEN EXIT;
  335.  
  336.     FOR i := 1 TO 20 DO
  337.       IF (Mod31.Name[i] < ' ') AND
  338.          (Mod31.Name[i] <> #0) THEN EXIT;
  339. {
  340.     FOR j := 1 TO 31 DO
  341.       FOR i := 1 TO 21 DO
  342.         IF (Mod31.Samples[j].Name[i] < ' ') AND
  343.            (Mod31.Samples[j].Name[i] <> #0) THEN EXIT;
  344. }
  345.     IF (Mod31.SongLen > 128) OR (Mod31.SongRep > 128) THEN EXIT;
  346.  
  347.  
  348.     { Processing of the header }
  349.  
  350.     Song.Status := msOK;
  351.  
  352.     Song.Name := FullHeap.HNewStr(StrASCIIZ(Mod31.Name, 20));
  353.  
  354.     Song.InitialTempo := 6;
  355.     Song.InitialBPM   := 125;
  356.     Song.Volume       := 255;
  357.  
  358.     FOR i := 0 TO 127 DO
  359.       INC(Mod31.PatternList[i]);
  360.     Move(Mod31.PatternList, Song.PatternSequence^, Mod31.SongLen);
  361.     Song.SequenceLength   := Mod31.SongLen;
  362.     Song.SequenceRepStart := Mod31.SongRep + 1;
  363.  
  364.     NumberOfPatterns := 0;
  365.     FOR i := 0 TO 127 DO
  366.       IF NumberOfPatterns < Mod31.PatternList[i] THEN
  367.         NumberOfPatterns := Mod31.PatternList[i];
  368.  
  369.  
  370.     { Processing of the patterns (the partiture) }
  371.  
  372.     ProcessPatterns(Song, St, NumberOfPatterns);
  373.     IF Song.Status > msOk THEN EXIT;
  374.  
  375.  
  376.     { Processing of the instruments }
  377.  
  378.     ProcessInstruments(Song, St, Mod31);
  379.     IF Song.Status > msFileTooShort THEN EXIT;
  380.   END;
  381.  
  382.  
  383.  
  384.  
  385. PROCEDURE LoadMod15(VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  386.   VAR
  387.     i     : WORD;
  388.     Mod31 : TModFile31 ABSOLUTE Header;
  389.     Mod15 : TModFile15 ABSOLUTE Header;
  390.   BEGIN
  391.     Move(Mod15.SongLen, Mod31.SongLen, 130);
  392.     FOR i := 16 TO 31 DO
  393.       FillChar(Mod31.Samples[i], SizeOf(Mod31.Samples[i]), 0);
  394.  
  395.     St.Seek(St.GetPos - SizeOf(TModFile31) + SizeOf(TModFile15));
  396.  
  397.     LoadMod(Song, St, Mod31);
  398.   END;
  399.  
  400.  
  401. PROCEDURE LoadModFileFormat  (VAR Song: TSong; VAR St: TStream; VAR Header: TSongHeader);
  402.   VAR
  403.     Mod31 : TModFile31 ABSOLUTE Header;
  404.   BEGIN
  405.     St.Seek(St.GetPos + SizeOf(TModFile31));
  406.  
  407.     IF (Mod31.Magic = Mod31MagicM_K_) THEN
  408.       BEGIN
  409.         IF Song.FileExt = '.WOW' THEN
  410.           BEGIN
  411.             Song.NumChannels := 8;
  412.             Song.FileFormat  := mffWow8;
  413.           END
  414.         ELSE
  415.           BEGIN
  416.             Song.NumChannels := 4;
  417.             Song.FileFormat  := mffMod31M_K_;
  418.           END;
  419.  
  420.         LoadMod(Song, St, Mod31);
  421.       END
  422.     ELSE IF (Mod31.Magic = Mod31MagicFLT4) THEN
  423.       BEGIN
  424.         Song.NumChannels := 4;
  425.         Song.FileFormat  := mffMod31FLT4;
  426.         LoadMod(Song, St, Mod31);
  427.       END
  428.     ELSE IF (Mod31.Magic = Mod31Magic6CHN) THEN
  429.       BEGIN
  430.         Song.NumChannels := 6;
  431.         Song.FileFormat  := mffFastTracker;
  432.         LoadMod(Song, St, Mod31);
  433.       END
  434.     ELSE IF (Mod31.Magic = Mod31Magic8CHN) THEN
  435.       BEGIN
  436.         Song.NumChannels := 8;
  437.         Song.FileFormat  := mffFastTracker;
  438.         LoadMod(Song, St, Mod31);
  439.       END
  440.     ELSE
  441.       BEGIN
  442.         Song.NumChannels := 4;
  443.         Song.FileFormat  := mffMod15;
  444.         LoadMod15(Song, St, Header);
  445.       END
  446.  
  447.   END;
  448.  
  449.  
  450.  
  451.  
  452. END.
  453.